package com.ruoyi.business.oss;

import cn.hutool.core.io.FileUtil;
import com.aliyun.oss.*;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.file.VedioUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author liumaotong
 * @Description 阿里云OSS储存服务
 * @Date 2023/7/14
 */
@Service
public class OssClientService {

    private static final Logger log = LoggerFactory.getLogger(OssClientService.class);

    private static final Long ONEMB = 1048576L;

    @Value("${oss.endpoint}")
    private String endpoint;
    @Value("${oss.accessId}")
    private String accessId;
    @Value("${oss.accessKey}")
    private String accessKey;
    //存储空间名称
    @Value("${oss.bucketName}")
    private String bucketName;
    @Value("${oss.viewurl}")
    private String viewurl;

    /**
     * @Author liumaotong
     * @Description 获取凭证
     * @Date 2023/7/11
     * @Param []
     * @return com.aliyun.oss.OSS
     */
    public OSS createOssClient(){
        //配置访问凭证
        CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessId, accessKey);
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, credentialsProvider);
        return ossClient;
    }

    /**
     * @Author liumaotong
     * @Description 创建储存空间，储存空间名称配置在yml中；
     * 执行本程序会先判断是否存在储存空间，如果不存在则会根据yml配置创建对应名称的储存空间，如果存在，则不会另行创建
     * @Date 2023/7/14
     * @Param []
     * @return java.lang.String
     */
    public String createBucket(){
        // 创建OSSClient实例。
        OSS ossClient = this.createOssClient();
        // 判断存储空间examplebucket是否存在。如果返回值为true，则存储空间存在，如果返回值为false，则存储空间不存在。
        boolean exists = ossClient.doesBucketExist(bucketName);
        if(!exists){
            // 创建CreateBucketRequest对象。
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
            // 创建存储空间。
            ossClient.createBucket(createBucketRequest);
        }
        if (ossClient != null) {
            ossClient.shutdown();
        }
        return bucketName;
    }

    /**
     * @Author liumaotong
     * @Description 创建目录
     * @Date 2023/7/14
     * @Param [directoryName]
     * @return java.lang.String
     */
    public void createDirectory(String directoryName){
        // 创建OSSClient实例。
        OSS ossClient = this.createOssClient();
        //创建目录
        try{
            String content = "";
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, directoryName, new ByteArrayInputStream(content.getBytes()));
            // 上传字符串。
            ossClient.putObject(putObjectRequest);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * @Author liumaotong
     * @Description 删除目录下文件
     * @Date 2023/9/12
     * @Param [prefix]
     * @return void
     */
    public void deleteDirrctoryAndFile(String prefix){
        OSS ossClient = this.createOssClient();
        try{
            // 删除目录及目录下的所有文件。
            String nextMarker = null;
            ObjectListing objectListing = null;
            do {
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                        .withPrefix(prefix)
                        .withMarker(nextMarker);

                objectListing = ossClient.listObjects(listObjectsRequest);
                if (objectListing.getObjectSummaries().size() > 0) {
                    List<String> keys = new ArrayList<String>();
                    for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                        System.out.println("key name: " + s.getKey());
                        keys.add(s.getKey());
                    }
                    DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url");
                    DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(deleteObjectsRequest);
                    List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
                    try {
                        for(String obj : deletedObjects) {
                            String deleteObj =  URLDecoder.decode(obj, "UTF-8");
                            System.out.println(deleteObj);
                        }
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }

                nextMarker = objectListing.getNextMarker();
            } while (objectListing.isTruncated());
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }


    /**
     * @Author liumaotong
     * @Description 根据文件url 删除文件
     * @Date 2024/3/1
     * @Param [url]
     * @return void
     */
    public void deleteFile(String url) {
        OSS ossClient = this.createOssClient();
        String urlWithoutExpires = url.substring(0,url.indexOf("?"));
        String objectName = urlWithoutExpires.replace("https://" + viewurl + "/","").replace("http://" + viewurl + "/","");
        // 删除文件或目录。如果要删除目录，目录必须为空。
        ossClient.deleteObject(bucketName, objectName);
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }


    /**
     * @Author liumaotong
     * @Description 批量删除指定文件
     * @Date 2024/1/12
     * @Param [keys]
     * @return void
     */
    public void deleteFilesByKeys(List<String> keys) throws Exception {
        //创建OSSClient实例。
        OSS ossClient  = this.createOssClient();
        try{
            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url"));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            try {
                for(String obj : deletedObjects) {
                    URLDecoder.decode(obj, "UTF-8");
                }
            } catch (UnsupportedEncodingException ex) {
                ex.printStackTrace();
                throw ex;
            }
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * @Author liumaotong
     * @Description 上传文件到OSS
     * @Date 2024/1/9
     * @Param [directoryName, objectName, file]
     * @return java.lang.String
     */
    public Map<String,String> upload(MultipartFile file, String directoryName) throws Exception {

//        log.info("======================= 上传文件Start =======================");

        Map<String,String> resMap = new HashMap<>(2);
        //创建OSSClient实例。
        OSS ossClient  = this.createOssClient();

        String uuid = IdUtils.fastSimpleUUID();
        File tempFile = new File(file.getOriginalFilename());

        File sourse = null;
        FileInputStream fileInputStream = null;

        FileUtils.copyInputStreamToFile(file.getInputStream(), tempFile);
        tempFile = FileUtil.rename(tempFile , uuid, true, true);
//        InputStream inputStream = new FileInputStream(tempFile );
        String fileName = tempFile.getName();
        String sourseName = file.getOriginalFilename();
        String contentType = getcontentType(sourseName.substring(sourseName.lastIndexOf(".")));

        //创建上传Object的Metadata
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(contentType);
        objectMetadata.setContentDisposition("inline;filename=" + fileName);
        try{
            //视频大于20MB则压缩一下
            if("vedio/mp4".equals(contentType) && file.getSize() > (ONEMB * 10)){
                sourse = new File(file.getOriginalFilename());
                FileUtils.copyInputStreamToFile(file.getInputStream(), sourse);
                VedioUtils.compre(sourse,tempFile,2);
            }
//            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, directoryName + "/" + fileName, inputStream,objectMetadata);
            fileInputStream = new FileInputStream(tempFile);
            ossClient.putObject(bucketName, directoryName + "/" + fileName, fileInputStream,objectMetadata);
            String url =  this.getUrl(directoryName + "/" + fileName);
            resMap.put("url",url);
            resMap.put("ossKey",directoryName + fileName);
        }catch (Exception e){
            e.printStackTrace();
            throw e;
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
            //删除临时文件
            tempFile.delete();
            if(null != sourse){
                sourse.delete();
            }
            if(null != fileInputStream){
                fileInputStream.close();
            }

        }

//        log.info("======================= 上传文件End =======================");
//        log.info("url:" + resMap.get("url"));

        return resMap;
    }

    private void setMeta(ObjectMetadata metadata){
         metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
        // 指定该Object的网页缓存行为。
         metadata.setCacheControl("no-cache");
        // 指定该Object被下载时的名称。
//         metadata.setContentDisposition("attachment;filename=oss_MultipartUpload.txt");
        // 指定该Object的内容编码格式。
//         metadata.setContentEncoding(OSSConstants.DEFAULT_CHARSET_NAME);
        // 指定初始化分片上传时是否覆盖同名Object。此处设置为true，表示禁止覆盖同名Object。
         metadata.setHeader("x-oss-forbid-overwrite", "false");
        // 指定上传该Object的每个part时使用的服务器端加密方式。
//         metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
        // 指定Object的加密算法。如果未指定此选项，表明Object使用AES256加密算法。
//         metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_DATA_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
        // 指定KMS托管的用户主密钥。
//         metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, "9468da86-3509-4f8d-a61e-6eab1eac****");
        // 指定Object的存储类型。
//         metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard);
        // 指定Object的对象标签，可同时设置多个标签。
//         metadata.setHeader(OSSHeaders.OSS_TAGGING, "a:1");
    }

    public static String getcontentType(String FilenameExtension) {
        if (FilenameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FilenameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
                FilenameExtension.equalsIgnoreCase(".jpg") ||
                FilenameExtension.equalsIgnoreCase(".png")) {
            return "image/jpg";
        }
        if (FilenameExtension.equalsIgnoreCase(".html")) {
            return "text/html";
        }
        if (FilenameExtension.equalsIgnoreCase(".txt")) {
            return "text/plain";
        }
        if (FilenameExtension.equalsIgnoreCase(".vsd")) {
            return "application/vnd.visio";
        }
        if (FilenameExtension.equalsIgnoreCase(".pptx") ||
                FilenameExtension.equalsIgnoreCase(".ppt")) {
            return "application/vnd.ms-powerpoint";
        }
        if (FilenameExtension.equalsIgnoreCase(".xls") ||
                FilenameExtension.equalsIgnoreCase(".xlsx")) {
            return "application/vnd.ms-excel";
        }
        if (FilenameExtension.equalsIgnoreCase(".docx") ||
                FilenameExtension.equalsIgnoreCase(".doc")) {
            return "application/msword";
        }
        if (FilenameExtension.equalsIgnoreCase(".xml")) {
            return "text/xml";
        }
        if (FilenameExtension.equalsIgnoreCase(".mp4") ||
                FilenameExtension.equalsIgnoreCase(".MP4") ||
                FilenameExtension.equalsIgnoreCase(".F4V") ||
                FilenameExtension.equalsIgnoreCase(".f4v")) {
            return "vedio/mp4";
        }
        return "image/jpg";
    }

    public String getUrl(String key) {
        // 设置URL过期时间为10年  3600l* 1000*24*365*10
        Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10);
        // 生成URL
        OSS ossClient = this.createOssClient();
        URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
        if (url != null) {
            String defaultEndpoint = bucketName + "." + endpoint;
            String resUrl = url.toString();
            return resUrl.replace(defaultEndpoint,viewurl);
        }
        return null;
    }


    public Map<String,Object> getSign(String appId) {
        String host = "https://" + bucketName + "." + endpoint;
        String dir = appId + "/";
        OSS client = this.createOssClient();
        Map<String, Object> respMap = new LinkedHashMap<String, Object>();
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            java.sql.Date expiration = new java.sql.Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = client.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = client.calculatePostSignature(postPolicy);

            respMap.put("accessid", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (client != null) {
                client.shutdown();
            }
        }
        return respMap;
    }
}
